package jmine.tec.report.impl.table;

import java.math.BigDecimal;

import jmine.tec.report.api.RendererTarget;
import jmine.tec.report.api.table.ReportCell;
import jmine.tec.report.api.table.ReportColumn;
import jmine.tec.report.api.table.ReportTable;
import jmine.tec.report.api.table.ReportTableGroupCell;
import bancosys.tec.utils.date.Date;

/**
 * Builder de agrupamentos de registros de tabelas.
 * 
 * @author lundberg
 * @param <V> tipo de registro
 */
public class ReportTableGroupBuilder<V> {

    private final ReportTable<V> table;

    private ReportTableGroupImpl<V> group;

    /**
     * {@inheritDoc}
     * 
     * @param table table
     * @param groupIndex groupIndex
     */
    public ReportTableGroupBuilder(ReportTable<V> table, Integer groupIndex) {
        this.table = table;
        this.group = new ReportTableGroupImpl<V>(table);
        this.createRPI(this.group).setGroupIndex(groupIndex);
    }

    /**
     * Adiciona uma coluna ao grupo. Mudanças nesta coluna entre 2 registros resultarão em uma quebra.
     * 
     * @param property property
     */
    public void addColumn(String property) {
        this.group.addColumn(new PropertyCellValueResolver<V>(property));
    }

    /**
     * Adiciona uma coluna ao grupo. Mudanças nesta coluna entre 2 registros resultarão em uma quebra.
     * 
     * @param resolver resolver
     */
    public void addColumn(CellValueResolver<V> resolver) {
        this.group.addColumn(resolver);
    }

    /**
     * Adiciona ao rodapé do grupo a soma da coluna, na mesma posição que a coluna.
     * 
     * @param columnId columnId
     * @param decimals decimals
     */
    public void addSum(String columnId, Integer decimals) {
        this.addFooterCell(columnId, new ReportTableGroupSum<V>(this.findColumn(columnId), decimals));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo string cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     */
    public void addHeaderDynamicString(String columnId, CellValueResolver<V> resolver) {
        ReportCell cell = new ReportCellImpl(null);
        this.addHeaderCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo date cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     * @param datePattern datePattern
     */
    public void addHeaderDynamicDate(String columnId, CellValueResolver<V> resolver, String datePattern) {
        ReportCell cell = new ReportCellImpl(null);
        cell.setRendererType(ReportTableBuilder.TYPE_DATE);
        if (datePattern != null) {
            this.createRPI(cell).setDatePattern(datePattern);
        }
        this.addHeaderCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo date cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     */
    public void addHeaderDynamicTimestamp(String columnId, CellValueResolver<V> resolver) {
        ReportCell cell = new ReportCellImpl(null);
        cell.setRendererType(ReportTableBuilder.TYPE_TIMESTAMP);
        this.addHeaderCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo number cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     * @param decimals decimals
     */
    public void addHeaderDynamicNumber(String columnId, CellValueResolver<V> resolver, Integer decimals) {
        ReportCell cell = new ReportCellImpl(null);
        cell.setRendererType(ReportTableBuilder.TYPE_NUMBER);
        this.createRPI(cell).setDecimals(decimals);
        this.addHeaderCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao rodapé um label, na mesma posição que a coluna.
     * 
     * @param columnId columnId
     * @param label label
     */
    public void addHeaderString(String columnId, String label) {
        this.addHeaderCell(columnId, new ReportTableGroupConstantCell<V>(new ReportCellImpl(label)));
    }

    /**
     * Adiciona ao rodapé um número constante, na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param number number
     * @param decimals decimals
     */
    public void addHeaderNumber(String columnId, BigDecimal number, Integer decimals) {
        ReportCell cell = new ReportCellImpl(number);
        cell.setRendererType(ReportTableBuilder.TYPE_NUMBER);
        this.createRPI(cell).setDecimals(decimals);
        this.addHeaderCell(columnId, new ReportTableGroupConstantCell<V>(cell));
    }

    /**
     * Adiciona ao rodapé um número constante, na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param date date
     * @param datePattern datePattern
     */
    public void addHeaderDate(String columnId, Date date, String datePattern) {
        ReportCell cell = new ReportCellImpl(date);
        cell.setRendererType(ReportTableBuilder.TYPE_DATE);
        if (datePattern != null) {
            this.createRPI(cell).setDatePattern(datePattern);
        }
        this.addHeaderCell(columnId, new ReportTableGroupConstantCell<V>(cell));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo string cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     */
    public void addFooterDynamicString(String columnId, CellValueResolver<V> resolver) {
        ReportCell cell = new ReportCellImpl(null);
        this.addFooterCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo date cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     * @param datePattern datePattern
     */
    public void addFooterDynamicDate(String columnId, CellValueResolver<V> resolver, String datePattern) {
        ReportCell cell = new ReportCellImpl(null);
        cell.setRendererType(ReportTableBuilder.TYPE_DATE);
        if (datePattern != null) {
            this.createRPI(cell).setDatePattern(datePattern);
        }
        this.addFooterCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao cabeçalho do grupo uma coluna com campo de tipo number cujo valor é dependente do primeiro registro do grupo.
     * 
     * @param columnId columnId
     * @param resolver resolver
     * @param decimals decimals
     */
    public void addFooterDynamicNumber(String columnId, CellValueResolver<V> resolver, Integer decimals) {
        ReportCell cell = new ReportCellImpl(null);
        cell.setRendererType(ReportTableBuilder.TYPE_NUMBER);
        this.createRPI(cell).setDecimals(decimals);
        this.addFooterCell(columnId, new ReportTableGroupDynamicCell<V>(cell, resolver));
    }

    /**
     * Adiciona ao rodapé um label, na mesma posição que a coluna.
     * 
     * @param columnId columnId
     * @param label label
     */
    public void addFooterString(String columnId, String label) {
        this.addFooterCell(columnId, new ReportTableGroupConstantCell<V>(new ReportCellImpl(label)));
    }

    /**
     * Adiciona ao rodapé um número constante, na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param number number
     * @param decimals decimals
     */
    public void addFooterNumber(String columnId, BigDecimal number, Integer decimals) {
        ReportCell cell = new ReportCellImpl(number);
        cell.setRendererType(ReportTableBuilder.TYPE_NUMBER);
        this.createRPI(cell).setDecimals(decimals);
        this.addFooterCell(columnId, new ReportTableGroupConstantCell<V>(cell));
    }

    /**
     * Adiciona ao rodapé um número constante, na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param date date
     */
    public void addFooterDate(String columnId, Date date) {
        ReportCell cell = new ReportCellImpl(date);
        cell.setRendererType(ReportTableBuilder.TYPE_DATE);
        this.addFooterCell(columnId, new ReportTableGroupConstantCell<V>(cell));
    }

    /**
     * Adiciona uma célula ao cabeçalho do grupo, que deve aparecer na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param groupCell groupCell
     */
    public void addHeaderCell(String columnId, ReportTableGroupCell<V> groupCell) {
        this.group.addHeaderCell(this.findColumn(columnId), groupCell);
    }

    /**
     * Adiciona uma célula ao rodapé do grupo, que deve aparecer na posição da coluna dada.
     * 
     * @param columnId columnId
     * @param groupCell groupCell
     */
    public void addFooterCell(String columnId, ReportTableGroupCell<V> groupCell) {
        this.group.addFooterCell(this.findColumn(columnId), groupCell);
    }

    /**
     * Termina a construção do grupo
     */
    public void close() {
        this.table.addGroup(this.group);
        this.group = null;
    }

    /**
     * Encontra a coluna com o id dado.
     * 
     * @param columnId columnId
     * @return ReportColumn
     */
    private ReportColumn<V> findColumn(String columnId) {
        for (ReportColumn<V> column : this.table.getColumns()) {
            if (column.getId().equals(columnId)) {
                return column;
            }
        }
        throw new IllegalStateException();
    }

    /**
     * Cria um RendererParameterInterface para o target
     * 
     * @param target target
     * @return RendererParameterInterface
     */
    private RendererParameterInterface createRPI(RendererTarget target) {
        return new RendererParameterInterface(target);
    }
}
